#include "xmodule_config.h"

#if MODULE_TOUCHPANEL_ENABLE && (TP_CHIP_TYPE == TP_CHIP_CST816X)

#include <string.h>

#include "app_util.h"

#if TPL_FIRMWARE_UPGRADE_SUPPORTED
#include TP_FIRMWARE_FILE
#endif

#ifndef FIRMWARE_VERSION_816D
#define FIRMWARE_VERSION_816D   0
#endif 

#ifndef FIRMWARE_VERSION_816S
#define FIRMWARE_VERSION_816S   0
#endif


#define CTP_NORMAL_SLAVE        0x15
#define CTP_BOOT_SLAVE          0x6A 

#define CTP_DEV_ID_ADDR         (0xA7)
#define CTP_VER_ADDR            (0xA9)
#define CTP_SLEEP_ADDR          (0xE5)

#define CTP_DEV_ID_816D         (0xB6) 
#define CTP_DEV_ID_816S         (0xB4) 
#define CTP_DEV_ID_INVALID      (0xFF)

#define CTP_SLEEP_CMD           (0x03)

static uint8_t self_chip_id = 0;
                                                                                                 

static bool read_byte( uint8_t reg, uint8_t* data)
{
    uint8_t addr = reg;
    uint32_t ret = TP_APP_READ_BYTE( addr, data );
    if( ret != TP_I2C_SUCCESS )
    {
        LOG1("[TP]: read fail, ret = 0x%X", ret );
    }
    return ret == TP_I2C_SUCCESS ;
}
static bool write_byte( uint8_t reg, uint8_t data)
{
    uint8_t write[2] = {reg, data};
    return TP_APP_WRITE_BYTE( 0, write ) == TP_I2C_SUCCESS;
}
static bool read_bytes( uint8_t reg, uint8_t* data, uint8_t datalen)
{
    uint32_t ret = TP_APP_READ_BYTES(  reg, data, datalen ) ;
    if( ret != TP_I2C_SUCCESS)
    {
//        app_i2c_deinit( TP_I2C_ID );
//        xhal_sw_twi_clear_bus( &tp_sw_i2c ); 
//        extern void tp_i2c_init(void);
//        tp_i2c_init();
        
        LOG1("[TP]: r err = 0x%X", ret );
    }
    return ret == TP_I2C_SUCCESS; 
}

bool em_touch_panel_mounting(uint8_t* p_chip_id, uint8_t* p_version)
{
    bool result = false;
    uint8_t chip_id = 0xff;
    uint8_t version = 0xff;

    
    TP_SET_RESET;
    TP_OS_DELAY_MS( 2 );
    TP_SET_nRESET;
    TP_OS_DELAY_MS( 25 );
    
    TP_I2C_SLAVE_ADDR_SET( CTP_NORMAL_SLAVE );
    for( int i = 0 ; i < 10 ; i++)
    {
        if( read_byte( 0xA7, &chip_id ) )
        {
            LOG1("[TP]: chip id = 0x%X", chip_id ); 
            if( chip_id == CTP_DEV_ID_816D // 816D
            ||  chip_id == CTP_DEV_ID_816S // 816S
            )
            {
                result = true;
                break;
            }
        }
        TP_OS_DELAY_MS( 20 );
    }
    
    if( result ) 
    {
        if( read_byte( 0xA9, &version ) )
        {
            LOG1("[TP]: version = %d", version );
            if( ((chip_id == CTP_DEV_ID_816D) && (version == FIRMWARE_VERSION_816D ))
//              ||((chip_id == CTP_DEV_ID_816S) && (version == FIRMWARE_VERSION_816S  ))
            )
            {
                *p_chip_id = chip_id;
                *p_version = version;
                self_chip_id = chip_id;
                tp_enable();
                return true;
            }
        }
        else 
        {
            LOG1("[TP]: not get version, but chip id = 0x%X", chip_id);
        }
    }
    else 
    {
        LOG1("[TP]: not get chip id, id = 0x%X", chip_id);
    }

    return false;
}

void em_touch_panel_update(void)
{
    uint8_t data[4];
    TP_I2C_SLAVE_ADDR_SET( CTP_NORMAL_SLAVE );
    if( read_bytes( 0x03, data, 4 ))
    {        
        if( (data[0] & 0xC0) != 0x40 )
        {
            touch_raw_data_t* p = tp_fifo_get_write();
            p->flag = TP_ALGO_FLAG_DOWN;
            p->x = data[1];
            p->y = data[3];
        }
    }
}


void em_touchpanel_enable(void)
{
    if( self_chip_id == CTP_DEV_ID_816D )
    {
        tp_enable();
    }
}

void em_touchpanel_disable(void)
{    
    if( self_chip_id == CTP_DEV_ID_816D )
    {
        TP_I2C_SLAVE_ADDR_SET( CTP_NORMAL_SLAVE );;
        write_byte( CTP_SLEEP_ADDR, CTP_SLEEP_CMD );
        
        tp_disable();
    }  
}



#if EM_TP_UPGRADE_REQUIRED // update 
#define TP_PKT_LEN      512
#define TP_FIRMWARE_OFFSET  6 
typedef struct 
{
    uint8_t*    pcontext;
    uint32_t    start_address;
    uint32_t    size;
    uint32_t    crc;
}tp_boot_ctl_t;

static bool boot_enter(void)
{
//    TP_SET_RESET;
//    TP_OS_DELAY_MS(10);
    TP_SET_nRESET;
    TP_OS_DELAY_MS(10);

    uint8_t res ;
    int retry_cnt = 10;

    uint16_t reg;
    uint8_t write_data[3];
    self_chip_id = CTP_DEV_ID_INVALID;
    
    while (retry_cnt --)
    {
        reg = 0xA001;
        write_data[0] = 0xAB;
        if( TP_BOOT_WRITE_BYTES( reg, write_data, 1) != TP_I2C_SUCCESS )
        {
            TP_OS_DELAY_MS( 2 );
            continue;
        }
        reg = 0xA003;
        if( TP_BOOT_READ_BYTES( reg, &res, 1) != TP_I2C_SUCCESS )
        {
            TP_OS_DELAY_MS( 2 );
            continue;
        }
        
        LOG1("[TP-BOOT]: chip id = 0x%X", res );
        if( res == 0xC1 )
        {
            self_chip_id = CTP_DEV_ID_816D;            
            return true;
        }
        else if( res == 0x55 )
        {
            self_chip_id = CTP_DEV_ID_816S;
            return true;
        }
        else 
        {
            continue;
        }
    }
    
    return false;
}

static bool boot_set_chip_compute_checksum(void)
{
    uint16_t reg = 0xA003;
    uint8_t data = 0x00;
    return TP_BOOT_WRITE_BYTES( reg, &data, 1 ) == TP_I2C_SUCCESS;
}
static bool boot_get_chip_compute_checksum(uint32_t* checksum)
{
    uint16_t reg = 0xA008;
    return TP_BOOT_READ_BYTES( reg, (uint8_t*)checksum, 2 ) == TP_I2C_SUCCESS;
}
static bool boot_write_firmware(uint32_t address, uint8_t *data, uint16_t size)
{
    uint16_t reg ;
    {
        reg = 0xA014;
        uint8_t write[2] = { address & 0xff, address >> 8};
        TP_BOOT_WRITE_BYTES( reg, write, 2);
//        TP_DELAY_MS(1);
    }

    {
        reg = 0xA018;
        TP_BOOT_WRITE_BYTES( reg, data, size );
//        TP_DELAY_MS(1);
    }

    {
        uint8_t write[1] = { 0xEE };
        reg = 0xA004;
        
        TP_BOOT_WRITE_BYTES( reg, write, 1 );
    }
    return true;
}
static bool boot_wait_idle(void)
{
    uint8_t res = 0;
    int retry_cnt = 50;

    uint16_t reg = 0xA005;
    while (retry_cnt --)
    {
        if( TP_BOOT_READ_BYTES( reg, &res, 1) == TP_I2C_SUCCESS)
        {
            if( res == 0x55 )
            {
                return true;
            }
            TP_OS_DELAY_MS( 10 );
        }
    }
    
    return false;
}
static bool boot_exit(void)
{
//    uint8_t write[3] = { 0xA0, 0x03, 0x00 };
    uint16_t reg = 0xA003; 
    uint8_t write[1] = {0x00}; 
    return TP_BOOT_WRITE_BYTES( reg, write, 1) == TP_I2C_SUCCESS;
}
static void boot_reset(void)
{
    TP_SET_RESET;
    TP_OS_DELAY_MS( 10 );
    TP_SET_nRESET;
    TP_OS_DELAY_MS( 30 ); 
}



void em_touch_panel_boot_update(uint32_t firmware_address, uint8_t *ppercent )
{
    *ppercent = 0;
//    iqw_module_wdt_restart();      
    TP_BOOT_SET_SLAVE_ADDR;
    
    if( !boot_enter() )
    {
        LOG0("[TP-BOOT]: enter fail");
        return ;
    }
    else 
    {
        LOG0("[TP-BOOT]: enter success");
    }

    uint32_t checksum = 0;
    boot_set_chip_compute_checksum();
    TP_OS_DELAY_MS( 500 );

    if( !boot_get_chip_compute_checksum( &checksum) )
    {
        LOG0("[TP-BOOT]: get checksum fail");
        return;
    }
    else 
    {
        LOG1("[TP-BOOT]: get checksum success, 0x%X", checksum );
    }
    
    // confirmm firmware address 
    if( firmware_address == 0 )
    {
        if( self_chip_id == CTP_DEV_ID_816D )
        {
            firmware_address = (uint32_t)app_bin_816d;
        }
//        else if( self_chip_id == CTP_DEV_ID_816S )
//        {
//            firmware_address = (uint32_t)app_bin_816s;
//        }
        else 
        {
            LOG0("[TP-BOOT]: no valid firmware"); 
            return; 
        }
    }
    
    // check firmware 
    uint8_t buf[ TP_PKT_LEN];
    tp_boot_ctl_t boot_ctl;
    boot_ctl.pcontext = (uint8_t*)firmware_address ;
    boot_ctl.start_address = uint16_decode( boot_ctl.pcontext );
    boot_ctl.size           = uint16_decode( boot_ctl.pcontext + 2);
    boot_ctl.crc            = uint16_decode( boot_ctl.pcontext + 4);
    
    if( checksum == boot_ctl.crc )
    {
        LOG0("[TP-BOOT]: firmware CRC same, exit"); 
        return;
    }
    boot_ctl.pcontext += TP_FIRMWARE_OFFSET;
    if( boot_ctl.size < 0x100 || boot_ctl.size > 0x6000 )
    {
        return;
    }

    boot_enter();
    // start upload 
    do 
    {
        // wdt restart 
//        iqw_module_wdt_restart();
        
        memcpy( buf , boot_ctl.pcontext + boot_ctl.start_address, TP_PKT_LEN );
        if( !boot_write_firmware( boot_ctl.start_address, buf, TP_PKT_LEN) )
        {
            break;
        }
        boot_ctl.start_address += TP_PKT_LEN;
        *ppercent = boot_ctl.start_address * 100 / boot_ctl.size ;
//        LOG1("[TP-BOOT]: write percent %d%%", *ppercent );
        
        TP_OS_DELAY_MS( 100 );
        if( !boot_wait_idle() )
        {
            break;
        }

    }while( boot_ctl.start_address < boot_ctl.size );

    boot_set_chip_compute_checksum();
    TP_OS_DELAY_MS( 500 );
    boot_get_chip_compute_checksum( &checksum );
    if( checksum != boot_ctl.crc )
    {

    }

    boot_exit();
    boot_reset();
}

#endif

#endif // !MODULE_TOUCHPANEL_ENABLE && (TP_CHIP_TYPE == TP_CHIP_CST816X)
